package yb.ecp.fast.user.service;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import yb.ecp.fast.feign.FastGenClient;
import yb.ecp.fast.infra.infra.log.LogHelper;
import yb.ecp.fast.infra.util.ListUtil;
import yb.ecp.fast.infra.util.Ref;
import yb.ecp.fast.infra.util.StringUtil;
import yb.ecp.fast.user.dao.entity.*;
import yb.ecp.fast.user.dao.mapper.*;
import yb.ecp.fast.user.infra.AuthConstant;
import yb.ecp.fast.user.infra.ErrorCode;
import yb.ecp.fast.user.service.VO.*;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by john on 2017/10/31.
 */
@Service
public class ScepterService {
    @Autowired
    RoleMapper roleMapper;

    @Autowired
    RoleMenuMapper roleMenuMapper;

    @Autowired
    RoleAuthMapper roleAuthMapper;

    @Autowired
    private FastGenClient genClient;

    @Autowired
    private RoleService roleService;

    @Autowired
    private WorkspaceService workspaceService;

    @Autowired
    private TemplateService templateService;

    @Autowired
    private ProfileMapper profileMapper;

    @Autowired
    private ProfileService profileService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private DepartmentMapper departmentMapper;
    /**
     * 默认角色id
     */
    @Value("${role.default.roleId}")
    private String defaultRoleId;

    /**
     * 默认admin管理员id
     */
    @Value("${role.admin.roleId}")
    private String roleAdminId;

    /*
     * 增加一种角色
     */
    public ErrorCode addRole(RoleVO roleVO, Ref<String> roleIdRef, String userId) {
        Ref<String> spaceIdRef = new Ref<>("");
        ErrorCode queryCode = profileService.queryWorkspaceId(userId, spaceIdRef);

        if (ErrorCode.Success != queryCode) {
            return queryCode;
        }
        String spaceId = spaceIdRef.get();
        roleVO.setSpaceId(spaceId);
        return addRole(roleVO, roleIdRef);
    }

    /*
     * 增加一种角色
     */
    public ErrorCode addRole(RoleVO roleVO, Ref<String> roleIdRef) {
        if (StringUtil.isNullOrEmpty(roleVO.getName())) {
            LogHelper.error("角色名称为空！", ErrorCode.IllegalArument.getCode());
            return ErrorCode.IllegalArument;
        }

        if (StringUtil.isNullOrEmpty(roleVO.getDeptId())) {
            roleVO.setDeptId("0");
        }

        //校验用户名是否有效
        Long cnt = roleMapper.countByName(AuthConstant.EMPTY_ID, roleVO.getName(), roleVO.getSpaceId(), roleVO.getDeptId());
        if (cnt > 0) {
            LogHelper.error(ErrorCode.RoleNameExists.getDesc(), ErrorCode.RoleNameExists.getCode());
            return ErrorCode.RoleNameExists;
        }

        RoleDO roleDO = new RoleDO();
        BeanUtils.copyProperties(roleVO, roleDO);
        String roleId = genClient.newGuidText().getValue();
        roleDO.setId(roleId);

        if (null == roleVO.getType() || 0 == roleVO.getType()) {
            //如果没有设定角色类型，则认为是普通角色
            roleDO.setType(0);
        }
        roleDO.setChannel(0);
//        roleDO.setSpaceId(spaceId);
        if (0 == roleMapper.insert(roleDO)) {
            LogHelper.error("添加角色失败！", ErrorCode.FailedToInsertRecord.getCode());
            return ErrorCode.FailedToInsertRecord;
        }
        roleIdRef.set(roleId);
        return ErrorCode.Success;
    }


    /*
     * 编辑角色信息
     */
    public ErrorCode editRole(RoleVO roleVO, String userId) {
        //系统默认数据禁止操作
        if (roleAdminId.equals(roleVO.getId().trim()) || defaultRoleId.equals(roleVO.getId().trim())) {
            LogHelper.error("系统内置角色信息不可编辑！", ErrorCode.CannotEditSystemData.getCode());
            return ErrorCode.CannotEditSystemData;
        }

        if (StringUtil.isNullOrSpace(roleVO.getName())) {
            LogHelper.error("角色名称为空！", ErrorCode.FailedToUpdateRecord.getCode());
            return ErrorCode.FailedToUpdateRecord;
        }

        //获取工作空间ID，用于判断角色名称是否被占用
        Ref<String> spaceIdRef = new Ref<>("");
        ErrorCode queryCode = profileService.queryWorkspaceId(userId, spaceIdRef);

        if (ErrorCode.Success != queryCode) {
            return queryCode;
        }
        String spaceId = spaceIdRef.get();

        if (StringUtil.isNullOrEmpty(roleVO.getDeptId())) {
            //查询部门ID，如果角色属于部门下，则判断角色名称是否存在需要在部门内部判断
            RoleDO roleDO = roleMapper.selectByPrimaryKey(roleVO.getId());
            roleVO.setDeptId(roleDO.getDeptId());
        }

        Long cnt = roleMapper.countByName(roleVO.getId(), roleVO.getName(), spaceId, roleVO.getDeptId());
        if (cnt > 0) {
            LogHelper.error(ErrorCode.RoleNameExists.getDesc(), ErrorCode.RoleNameExists.getCode());
            return ErrorCode.RoleNameExists;
        }

        RoleDO roleDO = new RoleDO();
        BeanUtils.copyProperties(roleVO, roleDO);
        if (0 == roleMapper.updateByPrimaryKeySelective(roleDO)) {
            return ErrorCode.FailedToUpdateRecord;
        }
        return ErrorCode.Success;
    }

    /*
     * 删除一种角色
     */
    public ErrorCode deleteRole(String roleId) {
        Long cnt = roleMapper.countUserByRoleId(roleId);
        if (cnt > 0) {
            LogHelper.error(ErrorCode.RoleIsUsed.getDesc(), ErrorCode.RoleIsUsed.getCode());
            return ErrorCode.RoleIsUsed;
        }

        //系统默认数据禁止操作
        if (roleAdminId.equals(roleId) || defaultRoleId.equals(roleId)) {
            LogHelper.error("系统内置角色信息，不允许删除！", ErrorCode.CannotEditSystemData.getCode());
            return ErrorCode.CannotEditSystemData;
        }

        roleMapper.deleteByPrimaryKey(roleId);

        return ErrorCode.Success;
    }

    /**
     * 校验角色是否被使用<br>
     * 管理员用户不可以删除，默认被使用
     *
     * @param roleIds List<String> 用户ID集合
     * @return List<String> 被使用的用户ID集合
     */
    public List<String> checkRolesUsed(List<String> roleIds) {
        List<String> usedRoles = new ArrayList<>();
        for (String role : roleIds) {
            Long cnt = roleMapper.countUserByRoleId(role);

            if (0 < cnt || roleAdminId.equals(role)) {
                RoleVO roleVO = getRole(role);
                usedRoles.add(roleVO.getName());
            }
        }
        return usedRoles;
    }

    /**
     * 批量删除角色
     *
     * @param roleIds List
     * @return List
     */
    public List<String> deleteRoles(List<String> roleIds) {
        List<String> usedRoles = new ArrayList<>();
        for (String roleId : roleIds) {
            if (0 == roleMapper.deleteByPrimaryKey(roleId)) {
                RoleVO roleVO = getRole(roleId);
                usedRoles.add(roleVO.getName());
            }
            roleMenuMapper.deleteMenu(roleId);
            roleAuthMapper.deleteAuth(roleId);
        }

        return usedRoles;
    }

    /*
     * 根据角色id查询角色
     */
    public RoleVO getRole(String roleId) {
        RoleDO roleDO = roleMapper.selectByPrimaryKey(roleId);
        if (null == roleDO) {
            LogHelper.debug(roleId + "角色信息不存在！");
            return null;
        }
        RoleVO roleVO = new RoleVO();
        BeanUtils.copyProperties(roleDO, roleVO);
        if (!StringUtil.isNullOrEmpty(roleDO.getDeptId()) && !AuthConstant.DEFAULT_PARENT_DEPT_ID.equals(roleDO.getDeptId())) {
            DepartmentDO departmentDO = departmentMapper.selectById(roleVO.getDeptId());
            if (null != departmentDO) {
                roleVO.setDeptName(departmentDO.getName());
            }
        }
        return roleVO;
    }

    /*
     * 设置该角色的用户。通常是用角色来关联设置用户。
     */
    @Transactional(rollbackFor = Exception.class)
    public ErrorCode setRoleUser(String roleId, List<String> userIds) {
        roleMapper.deleteRoleUsers(roleId);

        for (String userId : userIds) {
            if (0 == roleMapper.checkUserRole(roleId, userId)) {
                roleMapper.addUserRoleRelation(roleId, userId, 0);
            }
        }
        return ErrorCode.Success;
    }

    /**
     * 给用户设置一个角色
     *
     * @param roleId String
     * @param userId String
     * @return ErrorCode
     */
    public ErrorCode setRoleUser(String roleId, String userId, int userFlag) {
        if (0 == roleMapper.checkUserRole(roleId, userId)) {
            roleMapper.addUserRoleRelation(roleId, userId, userFlag);
        }
        return ErrorCode.Success;
    }

    /**
     * 删除用户所有角色信息
     *
     * @param userId String
     * @return ErrorCode
     */
    public ErrorCode removeRoleByUser(String userId) {
        roleMapper.removeRoleByUser(userId);
        return ErrorCode.Success;
    }

    public ErrorCode removeRoleUser(String roleId, String userId) {
        roleMapper.removeUserRole(roleId, userId);
        return ErrorCode.Success;
    }

    public List<String> getUserIdByRoleId(String roleId) {
        return roleMapper.selectUserIdByRoleId(roleId);
    }

    /**
     * 查询工作空间下的所有角色
     *
     * @param channel int
     * @param type    int
     * @param spaceId String
     * @return List
     */
    public List<RoleVO> getAllRoles(int channel, Integer type, String spaceId) {
        List<RoleDO> doRoles = roleMapper.selectAll(channel, type, spaceId);

        List<RoleVO> voRoles = new ArrayList<>();
        for (RoleDO roleDO : doRoles) {
            RoleVO roleVO = new RoleVO();
            roleVO.setDeptName(getDeptNameByRole(roleDO));
            BeanUtils.copyProperties(roleDO, roleVO);
            voRoles.add(roleVO);
        }
        return voRoles;
    }

    /**
     * 查询角色所属部门名称
     *
     * @param roleDO RoleDO
     * @return String
     */
    private String getDeptNameByRole(RoleDO roleDO) {
        if (null == roleDO || StringUtil.isNullOrEmpty(roleDO.getDeptId()) || AuthConstant.DEFAULT_PARENT_DEPT_ID.equals(roleDO.getDeptId())) {
            return "";
        }
        DepartmentDO departmentDO = departmentMapper.selectById(roleDO.getDeptId());
        if (null == departmentDO) {
            LogHelper.debug(roleDO.getDeptId() + "部门信息不存在！");
            return "";
        }
        return departmentDO.getName();
    }

    /**
     * 查询部门下角色列表<br>
     * （中铁建,角色属于部门下）
     *
     * @param deptId String
     * @param userId String
     * @return ErrorCode
     */
    public ErrorCode queryRolesByDepartment(String deptId, String userId, Ref<List> rolesRef) {
        String workspaceId = "";
        if (StringUtil.isNullOrEmpty(deptId) || deptId.equals(AuthConstant.DEFAULT_PARENT_DEPT_ID)) {
            deptId = AuthConstant.DEFAULT_PARENT_DEPT_ID;

            Ref<String> workspaceIdRef = new Ref<>("");
            ErrorCode queryCode = profileService.queryWorkspaceId(userId, workspaceIdRef);

            if (ErrorCode.Success != queryCode) {
                return queryCode;
            }
            workspaceId = workspaceIdRef.get();
        }
        List<RoleDO> doRoles = roleMapper.queryRolesByDepartment(deptId, workspaceId);

        List<RoleVO> voRoles = new ArrayList<>();
        for (RoleDO roleDO : doRoles) {
            RoleVO roleVO = new RoleVO();
            BeanUtils.copyProperties(roleDO, roleVO);
            voRoles.add(roleVO);
        }
        rolesRef.set(voRoles);
        return ErrorCode.Success;
    }


    /**
     * 批量添加菜单角色关联关系
     *
     * @param roleMenuVO RoleMenuVO
     * @return ErrorCode
     */
    @Transactional(rollbackFor = Exception.class)
    public ErrorCode addMenu(RoleMenuVO roleMenuVO) {
        //新增角色权限菜单 先删后增
        roleMenuMapper.deleteMenu(roleMenuVO.getRoleId());

        for (Integer menu : roleMenuVO.getMenuIds()) {
            RoleMenuDO roleMenuDO = new RoleMenuDO();
            roleMenuDO.setRoleId(roleMenuVO.getRoleId());
            roleMenuDO.setMenuId(menu);
            roleMenuMapper.insertSelective(roleMenuDO);
        }
        return ErrorCode.Success;
    }

    /**
     * 批量新增角色菜单和权限
     *
     * @param roleMenuVO RoleMenuVO
     * @return ErrorCode
     */
    @Transactional(rollbackFor = Exception.class)
    public ErrorCode addMenusAuths(RoleMenuVO roleMenuVO) {

        ErrorCode errorCode;
        errorCode = this.addAuths(roleMenuVO);
        if (0 != errorCode.getCode()) {
            return errorCode;
        }
        errorCode = this.addMenu(roleMenuVO);

        if (0 != errorCode.getCode()) {
            return errorCode;
        }
        return errorCode;
    }

    /**
     * 批量添加用户权限
     *
     * @param roleMenuVO RoleMenuVO
     * @return ErrorCode
     */
    @Transactional(rollbackFor = Exception.class)
    public ErrorCode addAuths(RoleMenuVO roleMenuVO) {
        roleAuthMapper.deleteAuth(roleMenuVO.getRoleId());

        for (Integer auth : roleMenuVO.getAuthIds()) {
            RoleAuthDO roleAuthDO = new RoleAuthDO();
            roleAuthDO.setRoleId(roleMenuVO.getRoleId());
            roleAuthDO.setAuthId(auth);
            roleAuthMapper.insertSelective(roleAuthDO);
        }

        return ErrorCode.Success;
    }

    /**
     * 根据角色返回关联的菜单集合
     *
     * @param roleId String
     * @return List
     */
    public List<Integer> listMenuIdByRoleId(String roleId, String userId) {
        TemplateVO templateVO = templateService.queryTempByUserId(userId);
        if (null == templateVO) {
            return new ArrayList<>();
        }

        if (roleAdminId.equals(roleId.trim())) {
            return templateService.queryMenuByTemplate(templateVO.getId());
        }
        return roleMenuMapper.getMenuByRoleId(roleId);
    }

    /**
     * 查询角色对应的菜单和权限集合
     *
     * @param roleId String
     * @return MenusAuthsVO
     */
    public MenusAuthsVO listMenuAuthByRoleId(String roleId, String userId) {
        TemplateVO templateVO = templateService.queryTempByUserId(userId);
        if (null == templateVO) {
            return null;
        }

        MenusAuthsVO menusAuthsVO = new MenusAuthsVO();
        List<Integer> tempMenus = templateService.queryMenuByTemplate(templateVO.getId());
        List<Integer> tempAuths = templateService.queryAuthByTemplate(templateVO.getId());

        //如果是管理员角色，查询模板对应的菜单和权限
        if (roleAdminId.equals(roleId.trim())) {
            menusAuthsVO.setMenus(tempMenus);
            menusAuthsVO.setAuths(tempAuths);
            return menusAuthsVO;
        }

        List<Integer> roleMenus = roleMenuMapper.getMenuByRoleId(roleId);
        List<Integer> roleAuths = roleMenuMapper.getAuthByRoleId(roleId);

        menusAuthsVO.setMenus(getIds(roleMenus, tempMenus));
        menusAuthsVO.setAuths(getIds(roleAuths, tempAuths));
        return menusAuthsVO;
    }

    /**
     * 取两个集合的交集
     *
     * @param listA List<Integer>
     * @param listB List<Integer>
     * @return List<Integer>
     */
    private List<Integer> getIds(List<Integer> listA, List<Integer> listB) {
        List<Integer> result = new ArrayList<>();

        for (Integer temp : listA) {
            if (listB.contains(temp)) {
                result.add(temp);
            }
        }
        return result;
    }


    /**
     * 根据用户ID 返回权限code集合
     *
     * @param userId String
     * @return List
     */
    public List<Integer> authCodesByUserId(String userId) {
        ProfileDO profileDO = profileMapper.selectById(userId);

        if (null == profileDO) {
            LogHelper.debug(userId + "用户信息不存在！");
            return new ArrayList<>();
        }

        List<Integer> authIds;
        List<String> roleIds = roleMapper.selectRoleIdsByUserId(userId);

        if (ListUtil.isNullOrEmpty(roleIds)) {
            roleIds.add(defaultRoleId);
        }

        //判断用户是否拥有管理员权限
        boolean adminFlag = roleService.ifAdminRole(roleIds);

        authIds = workspaceService.queryAuthsByWorkspace(profileDO.getSpaceId());
        if (adminFlag) {
            //查询模板权限作为该用户的权限
            return authIds;
        }
        //查询用户权限集合
        List<Integer> roleAuthIds = menuService.queryAuthIdsByRoleIds(roleIds);
        roleAuthIds.retainAll(authIds);
        return roleAuthIds;
    }

    /**
     * 过滤只在数组中菜单，采用广度优先遍历，不在数组中的元素删除
     */
    private void filterMenuList(List<MenuDisplayVO> menuDisplayVOList, List<Integer> menuIdList) {
        if (ListUtil.isNullOrEmpty(menuDisplayVOList)) {
            return;
        }
        int pos = menuDisplayVOList.size() - 1;
        for (; pos >= 0; --pos) {
            MenuDisplayVO menuDisplayVO = menuDisplayVOList.get(pos);
            if (menuIdList.contains(menuDisplayVO.getId())) {
                continue;
            }
            menuDisplayVOList.remove(pos);
        }
        for (MenuDisplayVO menuDisplayVO : menuDisplayVOList) {
            filterMenuList(menuDisplayVO.getChildren(), menuIdList);
        }

    }

    /**
     * 根据userId获取roleIds
     *
     * @param userId String
     * @return List<String>
     */
    public List<String> getRoleIdsByUserId(String userId) {
        List<String> roleIds = roleMapper.selectRoleIdsByUserId(userId);
        if (ListUtil.isNullOrEmpty(roleIds)) {
            LogHelper.debug(userId + "没有分配角色信息，默认内置角色！");
            roleIds.add(defaultRoleId);
        }
        return roleIds;
    }

    /**
     * 根据userId返回所属角色集合
     *
     * @param userId String
     * @return List
     */
    public List<RoleVO> getRolesByUserId(String userId) {
        List<RoleVO> roleVOs = new ArrayList<>();
        List<String> roleIds = getRoleIdsByUserId(userId);

        for (String roleId : roleIds) {
            roleVOs.add(getRole(roleId));
        }
        return roleVOs;
    }

    /**
     * 获取用户对应的菜单集合
     *
     * @param userId String
     * @param site   Integer
     * @return List
     */
    public List<MenuDisplayVO> listShownMenuByUser(String userId, Integer site) {
        List<MenuDisplayVO> shownMenuList = menuService.listShownMenu(site);
        //获取该用户下所有角色id
        List<String> ids = this.getRoleIdsByUserId(userId);
        if (ids.contains(roleAdminId)) {
            //包含管理员角色，返回所有菜单
            LogHelper.debug(userId + "拥有管理员角色权限！");
            return shownMenuList;
        }

        //根据该用户所有角色id获取菜单信息
        List<Integer> menuIdList = roleMenuMapper.getMenuIdsByRoleIds(ids);
        List<MenuDisplayVO> menuDisplayVOList = new ArrayList<>();
        for (MenuDisplayVO menuDisplayVO : shownMenuList) {
            if (menuIdList.contains(menuDisplayVO.getId())) {
                menuDisplayVOList.add(menuDisplayVO);//取交集
            }
        }
        return menuDisplayVOList;
    }

    /**
     * 查询用户可展示的菜单集合（树形结构）
     *
     * @param userId String
     * @return List
     */
//    @CaffeineLocalCache(keyExt = "'getShownMenuByUser:'+#userId", expireTime = 3000, refreshTime = 60)
    public List<MenuDisplayVO> getShownMenuByUser(String userId) {
        TemplateVO templateVO = templateService.queryTempByUserId(userId);

        if (null == templateVO) {
            return new ArrayList<>();
        }

        List<MenuDisplayVO> shownMenuList = menuService.getShownMenuTree(userId);

        //获取用户对应工作空间的所有menu
        List<Integer> tempMenuIds = templateService.queryMenuByTemplate(templateVO.getId());

        //获取该用户下所有角色id
        List<String> roleIds = this.getRoleIdsByUserId(userId);

        if (ListUtil.isNullOrEmpty(roleIds)) {
            roleIds.add(defaultRoleId);
        }

        List<Integer> menuIdList;

        if (roleIds.contains(roleAdminId)) {
            //用户拥有工作空间管理员menu
            LogHelper.debug(userId + "拥有管理员角色权限！");
            menuIdList = tempMenuIds;
        } else {
            //根据该用户所有角色id获取菜单信息
            menuIdList = roleMenuMapper.getMenuIdsByRoleIds(roleIds);
            tempMenuIds.retainAll(menuIdList);//取交集
        }

        //对应menuId筛选menu，封装菜单集合
        filterMenuList(shownMenuList, menuIdList);
        return shownMenuList;
    }
}
